// author: shelman
// description：封装了mqtt客户端的接口
#pragma once

#include <iostream>
#include <string>
#include <atomic>
#include <vector>
#include <mutex>
#include <stdint.h>

#include "export_def.h"

struct mosquitto;



// 通用宏定义
#define TOPIC_MAX_LEN               (128) // MQTT主题最大长度
#define TOPIC_QOS_MOSTONCE          (0) // 最多一次
#define TOPIC_QOS_LEASTONCE         (1) // 至少一次
#define TOPIC_QOS_ONLYONCE          (2) // 只有一次


// MQTT连接Broker的信息
struct MqttConnectInfo
{
    bool bAnonymous = true; // 是否匿名连接，匿名连接时，account和pwd不用填写，不检验
    uint16_t ui16KeepAliveInterval = 60; // 保活周期间隔
    uint16_t ui16BrokerPort = 1883; // MQTT Broker端口
    std::string strBrokerIp = "localhost"; // MQTT Broker IP信息
    std::string strAccount = ""; // 需要连接认证时的连接账户
    std::string strPwd = ""; // 需要连接认证时的连接账户对应密码
    void* pContext = nullptr; // 调用上下文实例
};

// MQTT主题信息
struct MqttTopicInfo
{
    int iQOS = 0; // 通信质量，0、1、2
    std::string strTopic = ""; // 消息主题

    // 构造函数
    MqttTopicInfo() {

    }

    // 构造函数
    MqttTopicInfo(const std::string& topic, int qos) : iQOS(qos), strTopic(topic) {

    }
};

// MQTT数据消息信息
struct MqttMessageInfo
{
    std::string strData = ""; // 数据内容
    MqttTopicInfo stTopicInfo; // 主题信息
};

/*****************************************************
 * 功能：MQTT消息处理回调函数类型
 * 输出参数：
 *      pCtx：调用的上下文实例
 *      stMqMsgInfo：MQTT数据消息
 * ***************************************************/
typedef void (*pfnMqttMsgCallback)(void *pCtx, const MqttMessageInfo& stMqMsgInfo);

/*****************************************************
 * 功能：MQTT客户端lib初始化，全局只调用一次
 * ***************************************************/
void API_EXPORT_DECL initMqttLibrary();

/*****************************************************
 * 功能：MQTT客户端lib反初始化，全局只调用一次
 * ***************************************************/
void API_EXPORT_DECL deinitMqttLibrary();


// MQTT客户端类
class API_EXPORT_DECL MqttClient
{
public:
    /*****************************************************
     * 功能：构造函数
     * ***************************************************/
    MqttClient();

    /*****************************************************
     * 功能：构造函数
     * 输入参数：
     *      stConInfo：连接信息
     * ***************************************************/
    MqttClient(const MqttConnectInfo& stConInfo);

    /*****************************************************
     * 功能：析构函数
     * ***************************************************/
    virtual ~MqttClient();

    /*****************************************************
     * 功能：设置连接信息
     * 输入参数：
     *      stConInfo：连接信息
     * ***************************************************/
    void setConnectInfo(const MqttConnectInfo& stConInfo);

    /*****************************************************
     * 功能：设置回调接口
     * 输入参数：
     *      pContext：调用上下文信息
     *      pfnMsgCb：MQTT消息处理回调函数指针
     * ***************************************************/
    void setCallbackFunc(void *pContext, pfnMqttMsgCallback pfnMsgCb);

    /*****************************************************
     * 功能：连接MQTT Broker服务
     * 输入参数：
     *      vtTopicInfos：需要订阅的主题信息集合
     *      pfnMsgCb：MQTT消息处理回调函数指针
     * ***************************************************/
    void connect(const std::vector<MqttTopicInfo>& vtTopicInfos, pfnMqttMsgCallback pfnMsgCb);

    /*****************************************************
     * 功能：断开与MQTT Broker服务的连接
     * ***************************************************/
    void disconnect();

    /*****************************************************
     * 功能：获取是否与MQTT Broker已连接
     * 返回值：
     *      bool：连接结果，true - 已连接，false - 未连接
     * ***************************************************/
    bool isConnected() const;

    /*****************************************************
     * 功能：订阅数据消息，需要等连接成功后，外部新增订阅的主题调用
     * 输入参数：
     *      strTopic：主题
     *      iQOS：质量
     * ***************************************************/
    void subscribe(const std::string& strTopic, int iQOS);

    /*****************************************************
     * 功能：取消订阅数据消息
     * 输入参数：
     *      strTopic：主题
     * ***************************************************/
    void unsubscribe(const std::string& strTopic);

    /*****************************************************
     * 功能：取消订阅数据消息
     * 输入参数：
     *      vtTopics：主题集合
     * ***************************************************/
    void unsubscribe(const std::vector<std::string>& vtTopics);

    /*****************************************************
     * 功能：发布数据消息
     * 输入参数：
     *      stMqMsgInfo：MQTT数据消息
     * ***************************************************/
    void publish(const MqttMessageInfo& stMqMsgInfo);

    /*****************************************************
     * 功能：处理连接状态，连接回调函数中调用
     * 输入参数：
     *      iRetCode：MQTT连接处理返回值
     * ***************************************************/
    void onConnect(int iRetCode);

    /*****************************************************
     * 功能：处理数据消息，数据回调函数中调用
     * 输入参数：
     *      stMqMsgInfo：MQTT数据消息
     * ***************************************************/
    void onMessage(const MqttMessageInfo& stMqMsgInfo);

private:
    /*****************************************************
     * 功能：订阅数据消息，连接成功后调用
     * 输入参数：
     *      stTopicInfo：主题信息
     * ***************************************************/
    void subscribeAfterConnected(const MqttTopicInfo& stTopicInfo);

    /*****************************************************
     * 功能：查找是否已经订阅过该主题了
     * 输入参数：
     *      strTopic：主题信息
     * 返回值：
     *      bool：查找结果，true - 已存在，false - 未存在
     * ***************************************************/
    bool queryTopicInfo(const std::string& strTopic);

private:
    bool m_bConnectflag = false; // 连接标志
    pfnMqttMsgCallback m_pfnMsgCb = nullptr; // 数据回调函数指针
    struct mosquitto *m_pMqttClient = nullptr; // mosquitto的实例
    MqttConnectInfo m_stConInfo; // 连接信息
    std::atomic<int> m_iMsgID; // 发布消息的index记录
    std::mutex m_mt4TopicInfos; // 订阅主题的信息集合锁
    std::vector<MqttTopicInfo> m_vtTopicInfos; // 订阅主题的信息集合
};

